/*
   Copyright 2008, Andres Postiglioni

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package com.aconcagua.beans {
  import com.aconcagua.context.binding.Binding;
  import com.aconcagua.context.binding.BindingException;
  import com.aconcagua.factory.BeanFactory;

   /**
   * @author Andres Postiglioni
   */
 public class BeanDefinitionSupport implements BeanDefinition {
    public static const SCOPE_PROTOTYPE: String = "prototype";
    public static const SCOPE_SINGLETON: String = "singleton";

    private var _name: String;
    private var _type: Class;
    private var _lazy: Boolean;

    private var _constructorArgs: Array; //of ConstructorArg;
    private var _properties: Array; //of Property
    private var _bindings: Array; //of Binding

    private var _scope:String;


    public function BeanDefinitionSupport(beanType: Class, theName:String = null) {
      _name = theName;
      _type = beanType;
      _constructorArgs = new Array()
      _properties = new Array();
      _bindings = new Array();
      scope = SCOPE_SINGLETON;
    }

    public function get name(): String {
      return _name;
    }

    public function set name(aName: String): void {
      _name = aName;
    }

    public function get type(): Class {
      return _type;
    }

    public function get lazy(): Boolean {
      return _lazy;
    }

    public function set lazy(isLazy: Boolean): void {
      _lazy = isLazy;
    }

    public function get scope(): String {
      return _scope;
    }

    [Inspectable(enumeration="singleton,prototype", defaultValue="singleton")]
    public function set scope(aScope: String): void {
      _scope = aScope;
    }

     public function set metadata(meta: Array): void {
      for each(var beanMetadata:BeanMetadata in meta) {
        beanMetadata.registerIn(this);
      }
    }

    public function get isSingleton(): Boolean {
      return scope == SCOPE_SINGLETON;
    }

    public function get isPrototype(): Boolean {
      return scope == SCOPE_PROTOTYPE;
    }

    public function addBinding(binding: Binding): void {
      if (_scope == SCOPE_PROTOTYPE) {
        throw new BindingException(_name);
      }
      _bindings.push(binding);
    }

    public function addConstructorArg(arg: ConstructorArg): void {
      _constructorArgs.push(arg);
    }

    public function addProperty(property: Property): void {
      _properties.push(property);
    }

    public function createInstance(factory: BeanFactory): Object {
      var args:Array = new Array();
      for each (var arg:ConstructorArg in _constructorArgs) {
        args.push(arg.getValue(factory));
      }

      //I know this is ugly, but as far as I know actionscript 3 does not support any other way
      switch(args.length) {
        case  0: return new type();
        case  1: return new type(args[0]);
        case  2: return new type(args[0], args[1]);
        case  3: return new type(args[0], args[1], args[2]);
        case  4: return new type(args[0], args[1], args[2], args[3]);
        case  5: return new type(args[0], args[1], args[2], args[3], args[4]);
        case  6: return new type(args[0], args[1], args[2], args[3], args[4], args[5]);
        case  7: return new type(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
        case  8: return new type(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
        case  9: return new type(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
        case 10: return new type(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
      }
      throw new Error("Cannot instantiate classes with more than 10 parameters in the constructor");
    }

    public function applyProperties(beanInstance: Object, factory: BeanFactory): void {
      this.setValues(beanInstance, factory);
      this.applyBindings(factory);
    }

    private function setValues(beanInstance: Object, factory: BeanFactory): void {
      for each(var property: Property in _properties) {
        property.setTo(beanInstance, factory);
      }
    }

    private function applyBindings(factory: BeanFactory): void {
      for each (var binding: Binding in _bindings) {
        binding.execute(factory);
      }
    }
  }
}